Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fancy rules for traversing with PrimMonad #146

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

treeowl
Copy link
Collaborator

@treeowl treeowl commented Apr 24, 2018

This isn't ready yet, but it'll probably be easier to discuss as a PR.

@treeowl
Copy link
Collaborator Author

treeowl commented Apr 24, 2018

@andrewthad, these rules actually do seem to work, but I haven't figured out the non-PrimMonad base monad case yet. It seems like you have a better sense of mmorph and the like, so maybe you'll have some ideas.

-> Array a
-> f (Array b)
traverseArrayWonk _ f = traverseArray f
{-# INLINE [0] traverseArrayWonk #-}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: if it would help, we could add another wonky parameter to this function to handle Maybe, Either, Identity, and the like. I just haven't yet been able to figure out how that part should work at all.

@andrewthad
Copy link
Collaborator

I've been mulling over an approach that reifies the dictionary instead of passing it around as a class constraint. I think this would permit base monads other than IO and ST, but I haven't had the chance to actually try writing it out yet.

@bgamari
Copy link
Contributor

bgamari commented Apr 29, 2018

I'll admit, I'm a bit concerned that this is raising the level of abstraction a bit higher than is appropriate in primitive. Historically primitive has essentially been nothing more than a set of wrappers around GHC's primops. I think this is a very useful point in the design space since its behavior is clear, predictable, and not subject to the whims of the simplifier.

I can certainly see the value in having a set of higher-level interfaces, but I do wonder whether this might not be better explored in a new library.

@treeowl
Copy link
Collaborator Author

treeowl commented Apr 29, 2018 via email

@cartazio
Copy link
Contributor

cartazio commented Apr 29, 2018 via email

@cartazio
Copy link
Contributor

cartazio commented Apr 29, 2018 via email

@treeowl
Copy link
Collaborator Author

treeowl commented Apr 29, 2018

@cartazio, I generally agree with your last statement. With regard to traverse, I think the best approach is likely to optimize a few good categories of use and document both what gets optimized and what to do about cases that will need to be specialized by hand. But I also think we should probably suspend a lot of the detailed discussion of yes or no until @andrewthad and I have a better sense of the bounds of what we can do well. This particular PR is pretty small and conservative in my opinion. I'd like to see if it's possible to get similarly good results for the pure cases Andrew has focused on in a similarly simple way, and to look also at compositions.

As for vector: I suspect everything will be somewhat harder, with more complicated trade-offs. We can only do things that fit sufficiently well into the fusion framework. There is surely room for improvement there (of a similar general nature), but I think that layers additional complexity on top of what we're looking at here.

@andrewthad
Copy link
Collaborator

I spent some time this weekend trying to explore this space further, and I cannot come up with anything easy to reason about that makes types like Maybe, Identity, and Either work as the base monad for transformer stacks.

I have similar concerns about trying to do something like this in vector. The stream fusion framework is difficult to work with, and I'm not sure if this would work in that setting.

Concerning whether or not it's good to even have the instances, I have found that having them is useful. As I have started using Array in my own libraries' data structures more and more, I've found that these instances allow GHC's powerful deriving mechanism to do a lot of work for me (especially concerning Eq, Show, Functor, and Traversable instances).

I'm content to hold off on committing to anything here until the space is better understood. In practice, I think that having IO and ST be rewritten is useful, and I think that having Maybe be rewritten is useful (I use Maybe a lot). For any (sufficiently affine) monad transformer stack on top of IO or ST, people can just use traverseArrayP and not have to rely on a rule. For Maybe, it's trickier to do manually, but it's still possible.

@andrewthad
Copy link
Collaborator

Thinking about this more, I think that the direction I would prefer this to take would be to just include explicitly specialized traversals for various monads. That is, just having:

  • traverseArrayMaybe
  • traverseArrayEither
  • traverseArrayIO
  • etc.

This is tedious, but at least it's really clear to the user what kind of behavior they are going to get.

@cartazio
Copy link
Contributor

cartazio commented Jun 12, 2020 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants